package it.uniroma2.art.coda.imdb;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelCreationException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.UnsupportedRDFFormatException;
import it.uniroma2.art.owlart.io.RDFFormat;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTStatement;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.OWLArtModelFactory;
import it.uniroma2.art.owlart.models.OWLModel;
import it.uniroma2.art.owlart.models.RDFModel;
import it.uniroma2.art.owlart.models.TransactionBasedModel;
import it.uniroma2.art.owlart.models.UnloadableModelConfigurationException;
import it.uniroma2.art.owlart.models.UnsupportedModelConfigurationException;
import it.uniroma2.art.owlart.models.conf.BadConfigurationException;
import it.uniroma2.art.owlart.models.conf.ModelConfiguration;
import it.uniroma2.art.owlart.navigation.ARTStatementIterator;
import it.uniroma2.art.owlart.sesame2impl.factory.ARTModelFactorySesame2Impl;
import it.uniroma2.art.owlart.sesame2impl.models.conf.Sesame2PersistentInMemoryModelConfiguration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DivideActors {

	private static Logger logger = LoggerFactory.getLogger(DivideActors.class);

	private String baseUri = "http://art.uniroma2.it/imdb";
	private String importedUri = "http://art.uniroma2.it/imdb";
	// private String rdfInputFilePath = "E:/CODATest/imdb/IMDB.owl";
	private String rdfInputFilePath = "E:/CODATest/imdb/IMDBOutPutComplete_test3.owl";
	private String rdfOutputFilePath = "E:/CODATest/imdb/IMDBOutPutComplete_rearranged2.owl";

	private String repositoryDirPath = "E:/CODATest/imdb/repDir";

	private boolean useAutocommit = false;

	private boolean useResoner = false;

	public static void main(String[] args) {
		logger.info("Program started");

		DivideActors divideActors = new DivideActors();

		OWLModel owlModel = null;
		long start = System.currentTimeMillis();

		try {
			// Create an OWLModel and the triple Store.
			owlModel = divideActors.initializeOWLModel();

			List<List<ARTResource>> actorClassesArray = divideActors.countAndDivideActors(owlModel);

			for (int i = 0; i < actorClassesArray.size(); ++i) {
				logger.info("Number of actor with " + i + " movie : " + actorClassesArray.get(i).size());
				if (actorClassesArray.get(i).size() < 6) {
					for (int k = 0; k < actorClassesArray.get(i).size(); ++k)
						logger.info("\t" + actorClassesArray.get(i).get(k));
				}
			}

			divideActors.rearrangeActors(actorClassesArray, owlModel);

			// save the ontology
			divideActors.saveRDF(owlModel);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (ModelUpdateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ModelAccessException e) {
			e.printStackTrace();
		} catch (UnsupportedRDFFormatException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (ModelCreationException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (UnsupportedModelConfigurationException e) {
			e.printStackTrace();
		} catch (UnloadableModelConfigurationException e) {
			e.printStackTrace();
		} catch (BadConfigurationException e) {
			e.printStackTrace();
		} finally {
			try {
				owlModel.close();
			} catch (ModelUpdateException e) {
				e.printStackTrace();
			}
			long end = System.currentTimeMillis();

			String prettyTime = "Program terminated after " + divideActors.printPrettyTime(start, end);
			logger.info(prettyTime);
		}

	}

	private OWLModel initializeOWLModel() throws ModelUpdateException, FileNotFoundException, IOException,
			ModelAccessException, UnsupportedRDFFormatException, ClassNotFoundException,
			ModelCreationException, InstantiationException, IllegalAccessException,
			UnsupportedModelConfigurationException, UnloadableModelConfigurationException,
			BadConfigurationException {

		/*
		 * String owlArtModelFactoryImplClassName =
		 * "it.uniroma2.art.owlart.sesame2impl.factory.ARTModelFactorySesame2Impl"; Class<? extends
		 * ModelFactory> owlArtModelFactoryImplClass = (Class<? extends ModelFactory>)
		 * Class.forName(owlArtModelFactoryImplClassName); OWLArtModelFactory artModelFact =
		 * OWLArtModelFactory.createModelFactory( owlArtModelFactoryImplClass.newInstance());
		 */
		OWLArtModelFactory artModelFact = OWLArtModelFactory
				.createModelFactory(new ARTModelFactorySesame2Impl());
		// String mcClassName =
		// "it.uniroma2.art.owlart.sesame2impl.models.conf.Sesame2PersistentInMemoryModelConfiguration";
		// Class mcClass = Class.forName(mcClassName);
		Class mcClass = Sesame2PersistentInMemoryModelConfiguration.class;
		ModelConfiguration modelConf = artModelFact.createModelConfigurationObject(mcClass);
		if (!useResoner) {
			modelConf.setParameter("directTypeInference", "false");
			modelConf.setParameter("rdfsInference", "false");
		}
		OWLModel owlModel = artModelFact.loadOWLModel(baseUri, repositoryDirPath, modelConf);
		owlModel.setBaseURI(baseUri);
		owlModel.setDefaultNamespace(baseUri + "#");
		owlModel.clearRDF();
		owlModel.addRDF(new File(rdfInputFilePath), importedUri, RDFFormat.RDFXML, NodeFilters.MAINGRAPH);

		return owlModel;
	}

	private List<List<ARTResource>> countAndDivideActors(RDFModel rdfModel) throws ModelAccessException {
		ArrayList<List<ARTResource>> actorClassArray = new ArrayList<List<ARTResource>>();
		for (int i = 0; i < 15; ++i) {
			actorClassArray.add(new ArrayList<ARTResource>());
		}

		ARTURIResource predType = rdfModel
				.createURIResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
		ARTURIResource objActor = rdfModel.createURIResource("http://art.uniroma2.it/imdb#Actor");
		// get the list of all the actor
		ARTStatementIterator artIter = rdfModel.listStatements(NodeFilters.ANY, predType, objActor, true,
				NodeFilters.ANY);
		ARTStatementIterator artIter2;
		int contActor = 0;
		ARTURIResource predHasActed = rdfModel.createURIResource("http://art.uniroma2.it/imdb#hasActedIn");
		System.out.println("");
		while (artIter.hasNext()) {

			if (contActor % 100 == 0) {
				System.out.print(".");

			}
			++contActor;

			ARTStatement artStatement = artIter.next();
			ARTResource subj = artStatement.getSubject();
			artIter2 = rdfModel.listStatements(subj, predHasActed, NodeFilters.ANY, false, NodeFilters.ANY);
			int contMovie = 0;
			while (artIter2.hasNext()) {
				artIter2.next();
				++contMovie;
			}
			actorClassArray.get(contMovie).add(subj);
		}
		System.out.println("");
		logger.info("number of actor = " + contActor);

		return actorClassArray;
	}

	public void rearrangeActors(List<List<ARTResource>> actorClassesArray, RDFModel rdfModel)
			throws ModelAccessException, ModelUpdateException {
		ARTURIResource predType = rdfModel
				.createURIResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
		if (!useAutocommit)
			((TransactionBasedModel) rdfModel).setAutoCommit(false);
		ARTURIResource actorClass = rdfModel.createURIResource("http://art.uniroma2.it/imdb#Actor");
		ARTURIResource subClassProp = rdfModel.createURIResource("http://www.w3.org/2000/01/rdf-schema#subClassOf");
		for (int i = 0; i < actorClassesArray.size(); ++i) {
			ARTURIResource newActorClass = rdfModel.createURIResource("http://art.uniroma2.it/imdb#"
					+ "Actor" + i + "Film");
			rdfModel.addTriple(newActorClass, subClassProp, actorClass);
			logger.info("Adding "+actorClassesArray.get(i).size()+" Actor to class: "+newActorClass.toString());
			for (int k = 0; k < actorClassesArray.get(i).size(); ++k) {
				ARTResource actorRes = actorClassesArray.get(i).get(k);
				ARTStatementIterator artIter = rdfModel.listStatements(actorRes, predType, NodeFilters.ANY,
						true, NodeFilters.ANY);
				while (artIter.hasNext()) {
					ARTStatement artStatement = artIter.next();
					ARTResource actorCurrentClass = artStatement.getObject().asResource();
					if (actorCurrentClass.toString().endsWith("Movie")) {
						rdfModel.removeType(actorRes, actorCurrentClass, NodeFilters.ANY);
					}
				}
				rdfModel.addType(actorRes, newActorClass);
			}
		}
		if (!useAutocommit)
			((TransactionBasedModel) rdfModel).commit();
	}

	public String printPrettyTime(long start, long end) {
		long secTotal = (end - start) / 1000;
		long sec = secTotal % 60;
		long minTotal = secTotal / 60;
		long min = minTotal % 60;
		long hourTotal = minTotal / 60;

		String prettyTime = "Program termnated after ";
		if (hourTotal < 10)
			prettyTime += "0";
		prettyTime += hourTotal + ":";
		if (min < 10)
			prettyTime += "0";
		prettyTime += min + ":";
		if (sec < 10)
			prettyTime += "0";
		prettyTime += sec;

		return prettyTime;
	}

	private void saveRDF(OWLModel owlModel) throws IOException, ModelAccessException,
			UnsupportedRDFFormatException {
		logger.info("saving rdf to : " + rdfOutputFilePath); // convert to DEBUG
		owlModel.writeRDF(new File(rdfOutputFilePath), RDFFormat.RDFXML, NodeFilters.MAINGRAPH);
	}
}
